﻿#define _CRT_SECURE_NO_WARNINGS 1
#include"Snake.h"
#include<stdbool.h>
 void Welcome()
{ 
	 Setpos(30, 10);
	 printf("欢迎来到贪吃蛇小游戏\n");
	 Setpos(30, 11);
	 printf("请用↑ ↓ ← →控制蛇的移动\n");
	 Setpos(30, 12);
	 printf("F3加速F4减速，加速可以获得更高的分数\n");
	 Setpos(30, 13);
	 printf("碰到墙或者自己都会结束游戏\n");
	 system("pause");
	 system("cls");

}
 //地图是由特殊图形方块围成的长为57 宽为27的正方形
 void DrawMap()
 {//先打印长
	 Setpos(0, 0);
	 for (int i = 0;i <58;i+=2)
	 {
		 wprintf(L"%c", Wall);
	 }
	 Setpos(0, 26);
	 for (int i = 0;i <58;i=i+2)
	 {
		 wprintf(L"%c", Wall);
	 }
	 for (int i =1;i<26;i++)
	 {
		 Setpos(0, i);
		 wprintf(L"%c", Wall);
	 }
	 for (int i = 1;i < 26;i++)
	 {
		 Setpos(56, i);
		 wprintf(L"%c", Wall);
	 }
	  
 }
 void pause()
 {
	 while (1)
	 {
		 Sleep(300);
		 if (KEY_PRESS(VK_SPACE))
		 {
			 break;
		 }
	 }
 }
 void Creatsnake(pSnake ps)//初始化蛇,设置蛇一开始有四个格子
 {
	 for (int i = 1;i<5;i++)
	 {
		 pSnakeNode newnode = (pSnakeNode)malloc(sizeof(SnakeNode));
		 if (newnode == NULL)
		 {
			 perror("malloc fail");
		 }
		 assert(newnode);
		 newnode->next = NULL;
		 newnode->x = POS_X+(i*2);
		 newnode->y = POS_Y;
		 if (ps->phead == NULL)
		 {
			 ps->phead = newnode;
		 }
		 else
		 {
			 newnode->next = ps->phead;
			 ps->phead = newnode;
		 }
	 }
	 pSnakeNode cur = ps->phead;
	 while (cur)
	 {
		 Setpos(cur->x, cur->y);
		 wprintf(L"%c", Body);
		 cur = cur->next;
	 }
	 ps->Sleeptime= 200;
	 ps->sorce = 0;
	 ps->_state = OK;
	 ps->_Dir = RIGHT;
	 ps->FOOD= 10;
 }
 void Creatfood(pSnake ps)
 {
	 //随机生成数字 并保证不和蛇身重合
	
	 int x = 0;int y = 0;
	 again:
	 do
	 {
		 x = rand() % 53 + 2;
		 y = rand() % 25 + 1;
	 } while (x % 2 == 0);
	 pSnakeNode cur = ps->phead;
	 while (cur)
	 {
		 if (x == ps->phead->x || y == ps->phead->y)
		 {
			 goto again;
		 }
		 cur = cur->next;
	 }
	 //将食物也看成蛇身结点  因为吃了食物蛇身会变长
	 pSnakeNode pfood = (pSnakeNode)malloc(sizeof(SnakeNode));
	 if (pfood == NULL)
	 {
		 perror("malloc()faill");
		 return;
	 }
	 else 
	 {
		 pfood->x = x;
		 pfood->y = y;
		 ps->pFood = pfood;
		 Setpos(pfood->x, pfood->y);
		 wprintf(L"%c", Food);
	 }
 }
 //设置坐标参数
 void Setpos(int x, int y)
 {
	 COORD pos = { x,y };
	 HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
	 SetConsoleCursorPosition(hOutput, pos);
 }
 
//游戏开始
void Gamestart(pSnake ps)
{
	//先进行本地化适配，以便可以打印出长字符
	// 
	//先初始化游戏，并隐藏光标，打印欢迎语
	//设置控制台的大小
	system("mode con cols=100 lines=30");
	system("title Snake");

	HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);//获取标准输出的句柄
	CONSOLE_CURSOR_INFO CursorInfo;
	GetConsoleCursorInfo(hOutput, &CursorInfo);//获取控制台光标信息
	CursorInfo.bVisible = false; //隐藏控制台光标
	SetConsoleCursorInfo(hOutput, &CursorInfo);//设置控制台光标状态
	Welcome();//打印欢迎界面
    DrawMap();//打印地图
	Creatsnake(ps);//初始化蛇
	Creatfood(ps);//创建食物
}
void Printgame()
{
	Setpos(64, 15);
	printf("不能穿墙，不能咬到自己\n");
	Setpos(64, 16);
	printf("用↑ ↓ ← →分别控制蛇的移动.");
	Setpos(64, 17);
	printf("F1 为加速，F2 为减速\n");
	Setpos(64, 18);
	printf("ESC ：退出游戏.space：暂停游戏.");
	Setpos(64, 20);
}
//判断下一个结点是不是食物
int Isnextfood(pSnakeNode nextnode, pSnake ps)
{
	if ((nextnode->x == ps->pFood->x) && (nextnode->y == ps->pFood->y))
	{
		return 1;
	}
		return 0;

}
//吃掉食物
void Eatfood(pSnakeNode nextnode, pSnake ps)
{//头插食物
	nextnode->next = ps->phead;
 	ps->phead = nextnode;
	//打印蛇
	pSnakeNode cur = ps->phead;
	while (cur)
	{
		Setpos(cur->x, cur->y);
		wprintf(L"%c", Body);
		cur = cur->next;
	}
	//吃了食物要加分
	ps->sorce += ps->FOOD;
	free(ps->pFood);
	//重新创建食物
	Creatfood(ps);
}
//被自己撞死
void KillByself(pSnake ps)
{
	pSnakeNode cur = ps->phead->next ;
	while (cur)
	{
		if (ps->phead->x == cur->x && ps->phead->y == cur->y)
		{
			ps->_state = KillBySelf;
			break;
		}
		cur = cur->next;
	}
}
//被墙撞死
void KillBywall(pSnake ps)
{
	pSnakeNode cur = ps->phead;
	while (cur)
	{
		if (cur->x <= 0 || cur->x >= 57 || cur->y <= 0 || cur->y >= 26)
		{
			ps->_state = KillByWall;
			break;
		}
		cur = cur->next;
	}
}
//下一步没吃到食物
void Nofood(pSnakeNode nextnode, pSnake ps)
{//没吃到食物正常将下一个结点加入 同时释放为结点并置为空格
	nextnode->next = ps->phead;
	ps->phead = nextnode;
	//打印蛇身
	pSnakeNode cur=ps->phead;
	while (cur->next ->next )//这里是因为多加了一个结点导致打印要少一个
	{
		Setpos(cur->x , cur->y );
		wprintf(L"%c", Body);
		cur = cur->next;
	}
	//最后一个结点打印空格并释放结点
	Setpos(cur->next->x, cur->next->y);
	printf("  ");
	free(cur->next);
	cur->next = NULL;
}
//蛇移动一步 会出现很多种情况，遇到食物，没遇到，撞墙，撞到自己
void Snakemove(pSnake ps)
{//先创建一个结点在下一步移动的位置
	pSnakeNode nextnode = (pSnakeNode)malloc(sizeof(SnakeNode));
	if (nextnode == NULL)
	{
		perror("SnakeMove()::malloc()");
		return;
	}
	//确定下⼀个节点的坐标，下⼀个节点的坐标根据，蛇头的坐标和⽅向确定
	switch(ps->_Dir )
	{
	case UP:
	{
		nextnode->x = ps->phead->x;
		nextnode->y = ps->phead->y - 1;
	}
	break;
	case DOWN :
	{
		nextnode->x = ps->phead->x;
		nextnode->y = ps->phead->y +1;
	}
	break;
	case LEFT :
	{
		nextnode->x = ps->phead->x-2;
		nextnode->y = ps->phead->y;
	}
	break;
	case RIGHT :
	{
		nextnode->x = ps->phead->x+2;
		nextnode->y = ps->phead->y;
	}
	break;
	}
	if (Isnextfood(nextnode, ps))
	{
		Eatfood(nextnode, ps);
	}
	else
	{
		Nofood(nextnode, ps);
	}
	KillByself(ps);
	KillBywall(ps);
	
}
void Gamerun(pSnake ps) 
{
	/*游戏运⾏期间，右侧打印帮助信息，提⽰玩家
		根据游戏状态检查游戏是否继续，如果是状态是OK，游戏继续，
		否则游戏结束。如果游戏继续，就是检测按键情况，
		确定蛇下⼀步的⽅向，或者是否加速减速，是否暂停或者退出游戏。
		确定了蛇的⽅向和速度，蛇就可以移动了。*/
	//游戏运行需要走一步检测一下状态  则用do while语句
	Printgame();
	do {//先打印一下游戏信息在右边
		Setpos(64, 10);
		printf("得分：%d ", ps->sorce );
		printf("每个食物的得分：%d分", ps->FOOD);
		//判断按了什么键
		if (KEY_PRESS(VK_UP) && ps->_Dir != DOWN)
		{
			ps->_Dir = UP;
		}
		else if (KEY_PRESS(VK_DOWN) && ps->_Dir != UP)
		{
			ps->_Dir = DOWN;
		}
		else if (KEY_PRESS(VK_LEFT) && ps->_Dir != RIGHT )
		{
			ps->_Dir = LEFT;
		}
		else if (KEY_PRESS(VK_RIGHT) && ps->_Dir != LEFT)
		{
			ps->_Dir = RIGHT ;
		}
		else if (KEY_PRESS(VK_SPACE))
		{
			pause();
		}
		else if (KEY_PRESS(VK_ESCAPE))
		{
			ps->_state = PAUSE ;
			break;
		}
		else if (KEY_PRESS(VK_F3))
		{
			if (ps->Sleeptime >= 50)
			{
				ps->Sleeptime -= 30;
				ps-> FOOD += 2;
			}
		}
		else if (KEY_PRESS(VK_F4))
		{
			if (ps->Sleeptime < 350)
			{
				ps->Sleeptime += 30;
				ps->FOOD -= 2;
				if (ps-> Sleeptime == 350)
				{
					ps->FOOD = 1;
				}
			}
		}
		//每走一步蛇要睡眠一段时间
		Sleep(ps->Sleeptime);
		//根据按键蛇要移动
		 Snakemove(ps);
	} while (ps->_state == OK);
}
 void Gameend(pSnake ps)
{
	 system("cls");
	 if (ps->_state == KillBySelf)
	 {
		 Setpos(30, 10);
		 printf("不是老弟这都能被自己撞死啊");
	 }
	 else if (ps->_state == KillByWall)
	 {
		 Setpos(30, 10);
		 printf("不是，这路这么平，没石粒啊");
	 }
	 else if (ps->_state == Exit)
	 {
		 Setpos(30, 10);
		 printf("不是这就不玩了？？？");
	 }
	 Setpos(30, 15);
	 printf("想再玩一把还是退出,Y或N\n");
	 //将数据清除
	 pSnakeNode cur=ps->phead ;
	 while (cur)
	 {
		 pSnakeNode prev = cur;
		 cur = prev->next;
		 free(prev);
	 }
}